package com.egg.auth.service.dept.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.egg.auth.entity.dept.Dept;
import com.egg.auth.entity.dept.DeptLinkUser;
import com.egg.auth.mapper.dept.DeptMapper;
import com.egg.auth.model.request.web.DeptPostOrPut;
import com.egg.auth.model.response.web.DeptTree;
import com.egg.auth.service.dept.IDeptLinkUserService;
import com.egg.auth.service.dept.IDeptService;
import com.egg.auth.service.role.IRoleLinkDeptService;
import com.egg.auth.service.role.IRoleService;
import com.egg.common.core.constant.CoreConstant;
import com.egg.common.core.constant.StringPool;
import com.egg.common.core.exception.CustomException;
import com.egg.common.core.model.entity.BaseEntity;
import com.egg.common.mybatis.service.impl.BaseServiceImpl;
import com.egg.common.util.ConvertUtil;
import com.egg.common.util.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;


@Slf4j
@Service
public class DeptServiceImpl extends BaseServiceImpl<DeptMapper, Dept> implements IDeptService {


    @Autowired
    private IDeptLinkUserService deptLinkUserService;
    @Autowired
    private IRoleService roleService;
    @Autowired
    private IRoleLinkDeptService roleLinkDataPerService;


    @Transactional(rollbackFor = Throwable.class)
    @Override
    public Long add(DeptPostOrPut add) {
        if (existName(add.getName())) {
            throw new CustomException("名称已存在");
        }
        Dept dept = JsonUtil.objToObj(add, Dept.class);
        Optional<Dept> pDept = baseFindById(add.getPid());
        if (pDept.isPresent()) {
            if (ObjectUtil.isNotEmpty(pDept.get().getAncestors())) {
                dept.setAncestors(pDept.get().getAncestors() + StringPool.COMMA + dept.getPid());
            } else {
                dept.setAncestors(dept.getPid().toString());
            }
        } else {
            dept.setPid(CoreConstant.DEFAULT_ID);
            dept.setAncestors(CoreConstant.DEFAULT_ID.toString());
        }
        baseSave(dept);
        return dept.getId();
    }

    private boolean existName(String name) {
        return existName(name, Optional.empty());
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public Long edit(Serializable id, DeptPostOrPut edit) {
        Dept exist = baseGetById(id);
        if (existName(edit.getName(), Optional.of(exist.getId()))) {
            throw new CustomException("名称已存在");
        }
        Dept dept = JsonUtil.objToObj(edit, Dept.class);
        Optional<Dept> pDept = baseFindById(dept.getPid());
        if (pDept.isPresent()) {
            //不允许设置为当前子集的子集中的任何一个否则会产生循环
            if (belongToSub(id)) {
                throw new CustomException("不允许变更为当前的子部门");
            }
            if (ObjectUtil.isNotEmpty(pDept.get().getAncestors())) {
                dept.setAncestors(pDept.get().getAncestors() + StringPool.COMMA + dept.getPid());
            } else {
                dept.setAncestors(dept.getPid().toString());
            }
        } else {
            //父级不存在,
            dept.setPid(CoreConstant.DEFAULT_ID);
            dept.setAncestors(CoreConstant.DEFAULT_ID.toString());
        }
        dept.setId(exist.getId());
        baseSave(dept);
        return dept.getId();
    }

    private boolean existName(String name, Optional<? extends Serializable> filterId) {
        if (ObjectUtil.isEmpty(name)) {
            return false;
        }
        LambdaQueryWrapper<Dept> qw = buildLambdaQw();
        qw.eq(Dept::getName, name);
        filterId.ifPresent(o -> qw.notIn(Dept::getId, o));
        return count(qw) > 0;
    }

    /**
     * 查询当前ID是否属于它自己的子集
     *
     * @param id
     * @return
     */
    private boolean belongToSub(Serializable id) {
        if (ObjectUtil.isEmpty(id)) {
            return false;
        }
        Set<String> subIds = findAllSubById(id).stream()
                .map(o -> o.getId().toString()).collect(Collectors.toSet());
        return subIds.contains(id.toString());
    }

    private List<Dept> findAllSubById(Serializable id) {
        if (ObjectUtil.isEmpty(id)) {
            return Collections.emptyList();
        }
        return findAllSubById(Arrays.asList(id));
    }

    private List<Dept> findAllSubById(Collection<? extends Serializable> ids) {
        if (ObjectUtil.isEmpty(ids)) {
            return Collections.emptyList();
        }
        LambdaQueryWrapper<Dept> qw = Wrappers.lambdaQuery();
        qw.and(wrapper -> {
            for (Serializable id : ids) {
                wrapper.or().apply(String.format("find_in_set(%s, ancestors)", id));
            }
        });
        return list(qw);
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public void del(Collection<? extends Serializable> ids) {
        if (ObjectUtil.isEmpty(ids)) {
            return;
        }
        //删除, 删除当前以及当前所有的子集
        Set<Long> currentAndSubIds = findAllSubAndCurrentById(ids).stream().map(o -> o.getId()).collect(Collectors.toSet());
        baseDel(currentAndSubIds);
        deptLinkUserService.delByDeptId(currentAndSubIds);
        roleLinkDataPerService.delByDeptId(currentAndSubIds);
    }

    private List<Dept> findAllSubAndCurrentById(Collection<? extends Serializable> ids) {
        if (ObjectUtil.isEmpty(ids)) {
            return Collections.emptyList();
        }
        LambdaQueryWrapper<Dept> qw = Wrappers.lambdaQuery();
        qw.and(wrapper -> {
            wrapper.in(BaseEntity::getId, ids);
            for (Serializable id : ids) {
                wrapper.or().apply(String.format("find_in_set(%s, ancestors)", id));
            }
        });
        return list(qw);
    }

    @Override
    public List<DeptTree> tree(
            Optional<String> keywords
            , Optional<Boolean> enabled
    ) {
        List<Dept> dbList;
        LambdaQueryWrapper<Dept> qw = buildLambdaQw();
        qw.orderByAsc(Dept::getSort);
        if (keywords.isPresent() || enabled.isPresent()) {
            keywords.ifPresent(o -> qw.and(wrapper ->
                    wrapper.like(Dept::getName, o)
            ));
            enabled.ifPresent(o -> qw.eq(Dept::getEnabled, o));
            dbList = findAllParentAndCurrentById(list(qw).stream().map(o -> o.getId()).collect(Collectors.toList()));
        } else {
            dbList = list(qw);
        }
        if (ObjectUtil.isEmpty(dbList)) {
            return Collections.emptyList();
        }
        List<Dept> topList = dbList.stream()
                .filter(o -> Objects.equals(o.getPid(), CoreConstant.DEFAULT_ID))
                .collect(Collectors.toList());
        Map<Long, List<Dept>> childrenGroup = dbList.stream()
                .filter(o -> !Objects.equals(o.getPid(), CoreConstant.DEFAULT_ID))
                .collect(Collectors.groupingBy(o -> o.getPid()));
        List<DeptTree> tree = new ArrayList<>();
        topList.forEach(o -> {
            DeptTree topTree = JsonUtil.objToObj(o, DeptTree.class);
            topTree.setChildren(convertTree(o, childrenGroup));
            tree.add(topTree);
        });
        return tree;
    }

    private List<Dept> findAllParentAndCurrentById(Collection<? extends Serializable> id) {
        if (ObjectUtil.isEmpty(id)) {
            return Collections.emptyList();
        }
        List<Dept> currentList = baseFindById(id);
        Set<Long> pIds = currentList.stream()
                .flatMap(o -> ConvertUtil.strToList(o.getAncestors(), Long.class).stream())
                .filter(o -> ObjectUtil.isNotEmpty(o))
                .map(o -> Long.parseLong(o.toString())).collect(Collectors.toSet());
        List<Dept> parentList = baseFindById(pIds);
        List<Dept> allList = new ArrayList<>();
        allList.addAll(currentList);
        allList.addAll(parentList);
        return allList;
    }

    /**
     * 转树
     *
     * @param pDept
     * @param childrenDeptGroup
     * @return
     */
    private List<DeptTree> convertTree(Dept pDept, Map<Long, List<Dept>> childrenDeptGroup) {
        List<Dept> childrenDept = childrenDeptGroup.getOrDefault(pDept.getId(), Collections.emptyList());
        if (ObjectUtil.isEmpty(childrenDept)) {
            return Collections.emptyList();
        }
        List<DeptTree> deptTreeList = new ArrayList<>();
        childrenDept.forEach(o -> {
            DeptTree deptTree = JsonUtil.objToObj(o, DeptTree.class);
            deptTree.setChildren(convertTree(o, childrenDeptGroup));
            deptTreeList.add(deptTree);
        });
        return deptTreeList;
    }

    @Override
    public List<Dept> findByUserId(Serializable userId) {
        List<DeptLinkUser> deptUser = deptLinkUserService.findByUserId(userId);
        if (ObjectUtil.isEmpty(deptUser)) {
            return Collections.emptyList();
        }
        return baseFindById(deptUser.stream().map(o -> o.getDeptId()).collect(Collectors.toSet()));
    }

    @Override
    public List<Dept> findEnabledByUserId(Serializable id) {
        List<DeptLinkUser> deptUser = deptLinkUserService.findByUserId(id);
        return findEnabledById(deptUser.stream().map(o -> o.getDeptId()).collect(Collectors.toSet()));
    }

    @Override
    public List<Dept> findEnabledById(Collection<? extends Serializable> id) {
        if (ObjectUtil.isEmpty(id)) {
            return Collections.emptyList();
        }
        LambdaQueryWrapper<Dept> qw = Wrappers.lambdaQuery();
        qw.eq(Dept::getEnabled, true);
        qw.in(Dept::getId, id);
        return list(qw);
    }

    @Override
    public List<Dept> findEnabledCurrentAndAllSubByUserId(Serializable userId) {
        if (ObjectUtil.isEmpty(userId)) {
            return Collections.emptyList();
        }
        List<Dept> enabledByUserId = findEnabledByUserId(userId);
        if (ObjectUtil.isEmpty(enabledByUserId)) {
            return Collections.emptyList();
        }
        return findEnabledCurrentAndAllSubById(enabledByUserId.stream().map(o -> o.getId()).collect(Collectors.toSet()));
    }

    private List<Dept> findEnabledCurrentAndAllSubById(Collection<? extends Serializable> ids) {
        if (ObjectUtil.isEmpty(ids)) {
            return Collections.emptyList();
        }
        LambdaQueryWrapper<Dept> qw = Wrappers.lambdaQuery();
        qw.eq(Dept::getEnabled, true);
        qw.and(wrapper -> {
            wrapper.in(Dept::getId, ids);
            for (Serializable id : ids) {
                wrapper.or().apply(String.format("find_in_set(%s, ancestors)", id));
            }
        });
        return list(qw);
    }

    @Override
    public Dept buildVirtualDept() {
        Dept dept = new Dept();
        dept.setId(CoreConstant.DEFAULT_ID);
        return dept;
    }

}
